package com.fdb.efp.nls.service.impl.sed.repay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fdb.efp.nls.service.facade.queue.NlsProcessBizService;
import com.fdb.efp.nls.service.facade.queue.NlsProcessRuleService;
import com.fdb.efp.nls.service.facade.sed.RepaySoltService;
import com.fdb.efp.nls.service.vo.queue.NlsProcessBizVO;
import com.fdb.efp.nls.service.vo.queue.NlsProcessRuleVO;
import com.fdb.basic.framework.core.exception.BizException;
import com.fdb.basic.framework.core.exception.HangException;
import com.fdb.basic.framework.core.threadpool.AbstractThreadTask;
import com.fdb.basic.framework.core.threadpool.ExecuteRtnInfo;
import com.fdb.basic.framework.core.util.DateUtility;
import com.fdb.basic.framework.core.util.SpringContextUtils;
import com.fdb.basic.framework.core.util.TraceUtil;
import com.fdb.efp.nls.service.facade.PrdAdaptedSoltService;
import com.fdb.efp.nls.service.vo.PrdAdaptedSoltVO;
import com.fdb.efp.esb.common.constant.MsLoanConstant;
import com.fdb.efp.nls.service.facade.AccLoanService;
import com.fdb.efp.nls.service.facade.LoanRepayDetailService;
import com.fdb.efp.nls.service.vo.AccLoanVO;
import com.fdb.efp.loan.service.vo.LoanRepayDetailVO;
import com.fdb.efp.nls.common.constant.PrdAdaptedSoltEnums;
import com.fdb.efp.nls.common.constant.QueueTaskEnums;
import org.apache.logging.log4j.ThreadContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;

/**
 * 需求编号：【2019D0519】（业务提的需求编号）
 * 问题编号：【xxxxxxx】（若不是因为修复问题而新增该类，则留空）
 * 开发人员：qiuyf
 * 创建日期：2019年5月29日 下午2:31:06
 * 功能描述：还款网贷队列处理类【挂起】
 */
@Service("repayQueueHangHandleService")
@Scope(scopeName = "prototype")
public class RepayQueueHangHandle extends AbstractThreadTask {

	private static Logger logger = LoggerFactory.getLogger(RepayQueueHangHandle.class);

	private LoanRepayDetailVO loanRepayDetailVO;

	public void setLoanRepayDetailVO(LoanRepayDetailVO loanRepayDetailVO) {
		this.loanRepayDetailVO = loanRepayDetailVO;
	}

	@Override
	public ExecuteRtnInfo call() throws Exception {
		this.doTask();
		return new ExecuteRtnInfo(null, null, null, null, true);
	}

	@Override
	public void doTask() throws HangException, BizException, Exception {
		String setlSeq = loanRepayDetailVO.getSetlSeq();
		TraceUtil.setTraceId(loanRepayDetailVO.getGlobalSerno());
		TraceUtil.setPlafmID(loanRepayDetailVO.getPlatfmId());
		TraceUtil.setPlafmUsrID(loanRepayDetailVO.getPlatfmUsrId());
		ThreadContext.put(TraceUtil.GLBLSRLNO, loanRepayDetailVO.getGlobalSerno());
		logger.info("*********************************************");
		logger.info("*            开始一笔挂起执行队列                 * ");
		logger.info("*********************************************");
		logger.info("* 申请流水号:【" + setlSeq + "】*");
		logger.info("* 借据号 : 【" + loanRepayDetailVO.getLoanNo() + "】  *");
		logger.info("* 客户名称 : 【" + loanRepayDetailVO.getCustName() + "】  *");
		logger.info("* 全局流水号 : 【" + loanRepayDetailVO.getGlobalSerno() + "】  *");
		logger.info("* 创建日期 : 【" + loanRepayDetailVO.getSetlCreateDt() + "】*");
		logger.info("*********************************************");
		try {
			String loanNo = loanRepayDetailVO.getLoanNo();
			logger.info("根据借据号【" + loanNo + "】查询借据信息开始----------------");
			AccLoanVO accLoanVO = new AccLoanVO();
			accLoanVO.setBillNo(loanNo);
			accLoanVO = accLoanService.queryByPk(accLoanVO);
			if(Objects.isNull(accLoanVO)) {
				throw new BizException("根据借据号【" + loanNo + "】，查询到的借据信息为空！");
			}
			logger.info("根据借据号【" + loanNo + "】查询借据信息结束----------------查询结果为："+JSON.toJSONString(accLoanVO));

			logger.info("根据产品ID和插槽状态查询产品插槽信息");
			PrdAdaptedSoltVO prdAdaptedSoltVo = new PrdAdaptedSoltVO();
			prdAdaptedSoltVo.setPrdId(accLoanVO.getPrdId());
			prdAdaptedSoltVo.setSoltStage(loanRepayDetailVO.getSoltStage());// 插槽阶段
			prdAdaptedSoltVo.setSoltState(PrdAdaptedSoltEnums.SlotStateEnum.ON.getCode());// 插槽状态
			List<PrdAdaptedSoltVO> prdAdaptedSoltVoList = prdAdaptedSoltService.queryByPrdIdAndState(prdAdaptedSoltVo);
			Objects.requireNonNull(prdAdaptedSoltVoList);

			logger.info("根据申请流水号:【" + setlSeq + "】查询网贷业务过程信息");
			NlsProcessBizVO nlsProcessBizVo = new NlsProcessBizVO();
			nlsProcessBizVo.setApplySeq(setlSeq);
			nlsProcessBizVo = nlsProcessBizService.queryByPk(nlsProcessBizVo);
			Objects.requireNonNull(nlsProcessBizVo);
			logger.info("根据申请流水号:【" + setlSeq + "】查询网贷业务过程信息,挂起次数为：" + nlsProcessBizVo.getHangTimes());

			logger.info("申请流水号:【" + setlSeq + "】的遍历产品插槽的信息开始");
			for (int i = 0; i < prdAdaptedSoltVoList.size(); i++) {
				PrdAdaptedSoltVO prdAdaptedSolt = prdAdaptedSoltVoList.get(i);
				String soltCode = prdAdaptedSolt.getSoltCode();
				String soltName = prdAdaptedSolt.getSoltName();
				String soltService = prdAdaptedSolt.getSoltService();// 插槽服务类
				int soltOrder = prdAdaptedSolt.getSoltOrder();

				// 根据插槽代码 映射
				Method methodSoltName = nlsProcessBizVo.getClass().getMethod("setSoltName" + soltOrder, String.class);
				Method methodSoltTime = nlsProcessBizVo.getClass().getMethod("setSoltTime" + soltOrder, String.class);
				Method methodSoltstate = nlsProcessBizVo.getClass().getMethod("setSoltState" + soltOrder, String.class);
				Method methodSoltstateForGet = nlsProcessBizVo.getClass().getMethod("getSoltState" + soltOrder);
				String state = (String) methodSoltstateForGet.invoke(nlsProcessBizVo);
				logger.info("根据插槽代码 ：【" + soltCode + "】映射处理类");
				if (state.equals(PrdAdaptedSoltEnums.SoltProcessStateEnum.HANG.getCode())) {
					logger.info("该插槽代码 ：【" + soltCode + "】的插槽过程状态为：【" + PrdAdaptedSoltEnums.SoltProcessStateEnum.HANG.getDesc() + "】");
					logger.info("更新挂起次数在原有次数上加1，且更新该插槽状态为待执行");
					// 更新该插槽状态为待执行
					methodSoltstate.invoke(nlsProcessBizVo, PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTORY.getCode());
					nlsProcessBizService.updateByPk(nlsProcessBizVo);
				} else if (state.equals(PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTORY.getCode())) {
					logger.info("该插槽代码 ：【" + soltCode + "】的插槽过程状态为：【" + PrdAdaptedSoltEnums.SoltProcessStateEnum.EXECUTORY.getDesc() + "】，无须处理！");
				} else {
					logger.info("该插槽代码 ：【" + soltCode + "】的插槽过程状态为：【" + state + "】，无须处理开始下一插槽！");
					continue;
				}

				nlsProcessBizVo.setSoltCode(soltCode);// 插槽代码
				nlsProcessBizVo.setSoltName(soltName);// 插槽名称
				nlsProcessBizVo.setSoltService(soltService);// 插槽服务类
				nlsProcessBizVo.setExceptionFlag(Boolean.FALSE);// 异常标志
				nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.EXECUTING.getCode());
				nlsProcessBizService.updateByPk(nlsProcessBizVo);

				// 更新当前插槽代码、当前插槽名称
				// 当前插槽代码 
				loanRepayDetailVO.setSoltCode(soltCode);
				// 当前插槽名称 
				loanRepayDetailVO.setSoltName(soltName);
				// 插槽名称 
				methodSoltName.invoke(nlsProcessBizVo, soltName);
				// 插槽开始时间 
				methodSoltTime.invoke(nlsProcessBizVo, DateUtility.getCurrAppDateTimeString());
				
				/**
				 * mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
				 * 将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
				 * @date 2019/06/15
				 * @author qiuyf
				 * 
				 * 【2019/06/17，需求要求不使用mycat分片】
				 * loanRepayDetailVO.setLoanNo(null);
				 */

				// 更新申请进件信息 
				loanRepayDetailService.updateByPk(loanRepayDetailVO);

				logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean开始");
				RepaySoltService slotServiceBean = (RepaySoltService) SpringContextUtils.getBean(soltService);
				logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean结束，该对象为：【" + slotServiceBean + "】，调用对应的服务开始。");
				/**
				 * 【变更原因】前面为了更新loanRepayDetailVO成功，将loanRepayDetailVO里面字段loanNo设置为空了，导致在插槽处理需要到loanNo的地方，从loanRepayDetailVO里面取到空值，从而出错
				 * 【变更操作】在执行插槽前，给loanNo重新赋值
				 * @date 2019/06/15
				 * @author qiuyf
				 * 
				 * 【2019/06/17，需求要求不使用mycat分片】
				 * loanRepayDetailVO.setLoanNo(loanNo);
				 */
				
				slotServiceBean.execute(nlsProcessBizVo, loanRepayDetailVO, soltOrder);
				logger.info("根据插槽服务 ：【" + soltService + "】映射处理Bean结束，该对象为：【" + slotServiceBean + "】，调用对应的服务结束。");
			}
			logger.info("申请流水号:【" + setlSeq + "】的遍历产品插槽的信息结束");

			logger.info("根据申请流水号:【" + setlSeq + "】更新业务流程表队列任务状态为执行成功开始");
			nlsProcessBizVo.setQueueTaskState(QueueTaskEnums.QueueTaskStateEnum.SUCCESS.getCode());

			logger.info("根据申请流水号:【" + setlSeq + "】更新网贷任务队列中队列任务状态为执行成功开始");
			// 查询规则过程信息 
			NlsProcessRuleVO nlsProcessRuleVO = new NlsProcessRuleVO();
			nlsProcessRuleVO.setApplySeq(setlSeq);
			nlsProcessRuleVO = nlsProcessRuleService.queryByPk(nlsProcessRuleVO);

			// 设置插槽结果信息
			loanRepayDetailVO.setSoltResultDetails(JSONObject.toJSONString(nlsProcessBizVo));
			// 设置规则结果信息 
			loanRepayDetailVO.setRuleResultDetails(JSONObject.toJSONString(nlsProcessRuleVO));
			// 设置结果码
			loanRepayDetailVO.setRspCode(QueueTaskEnums.RspInfoEnum.SUCCESS.getRspCode());
			// 设置结果信息 
			loanRepayDetailVO.setRspMsg(QueueTaskEnums.RspInfoEnum.SUCCESS.getRspMsg());
			// 设置审批状态信息为通过 
			loanRepayDetailVO.setWfApprSts(MsLoanConstant.NlsApplyStateEnum.PASS.getVALUE());
			// 设置申请最近修改时间 
			loanRepayDetailVO.setLastChgDt(DateUtility.getCurrAppDateTimeString());// 最近修改时间

			/**
			 * mycat中不允许修改作为分库依据的列，所以更新的时候去掉“loan_no”
			 * 将更新执行的bean中loanNo字段赋值为null，更新语句会检测是否为空，再做更新。
			 * @date 2019/06/15
			 * @author qiuyf
			 * 
			 * 【2019/06/17，需求要求不使用mycat分片】
			 * loanRepayDetailVO.setLoanNo(null);
			 */
			
			loanRepayDetailService.updateByPk(loanRepayDetailVO);
			logger.info("根据申请流水号:【" + setlSeq + "】更新还款明细信息中审批状态为通过结束");
			nlsProcessBizService.updateByPk(nlsProcessBizVo);
			logger.info("根据申请流水号:【" + setlSeq + "】更新业务流程表队列任务状态为执行成功结束");
		} catch (HangException e) {
			logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的挂起异常信息为：" + e.getMessage());
			throw e;
		} catch (BizException e) {
			logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的业务异常信息为：" + e.getMessage());
			throw e;
		} catch (Exception e) {
			logger.error("队列处理服务中申请流水号:【" + setlSeq + "】的其他异常信息为：" + e.getMessage());
			throw e;
		} finally {
			TraceUtil.clear();
		}
	}

	@Autowired
	@Qualifier("accLoanService")
	private AccLoanService accLoanService;
	@Autowired
	@Qualifier("prdAdaptedSoltService")
	private PrdAdaptedSoltService prdAdaptedSoltService;
	@Autowired
	@Qualifier("nlsProcessBizService")
	private NlsProcessBizService nlsProcessBizService;
	@Autowired
	@Qualifier("loanRepayDetailService")
	private LoanRepayDetailService loanRepayDetailService;
	@Autowired
	@Qualifier("nlsProcessRuleService")
	private NlsProcessRuleService nlsProcessRuleService;

}
